#ifndef CAEWEBSOCKET_H
#define CAEWEBSOCKET_H

#include <pthread.h>
#include <thread>
#include "CaeTcpSocket.h"
#include <map>
#include "../cae_3rd/WebSocket.h"
#include "../cae_common/CaeTimer.h"
#include "../libs/openssl/include/openssl/ssl.h"
#include "../libs/openssl/include/openssl/x509.h"

enum CaeWebSocketParseState {
    WS_NEW,
    WS_FRAME_HDR_1,
    WS_FRAME_HDR_LEN,
    WS_FRAME_HDR_LEN16_2,
    WS_FRAME_HDR_LEN16_1,
    WS_FRAME_HDR_LEN64_8,
    WS_FRAME_HDR_LEN64_7,
    WS_FRAME_HDR_LEN64_6,
    WS_FRAME_HDR_LEN64_5,
    WS_FRAME_HDR_LEN64_4,
    WS_FRAME_HDR_LEN64_3,
    WS_FRAME_HDR_LEN64_2,
    WS_FRAME_HDR_LEN64_1,

    WS_COLLECT_FRAME_KEY_1,
    WS_COLLECT_FRAME_KEY_2,
    WS_COLLECT_FRAME_KEY_3,
    WS_COLLECT_FRAME_KEY_4,

    WS_PAYLOAD
};

enum WB_OPCODE {
    WB_OPC_CONTINUATION = 0,
    WB_OPC_TEXT_FRAME = 1,
    WB_OPC_BINARY_FRAME = 2,
    WB_OPC_CLOSE = 8,
    WB_OPC_PING = 9,
    WB_OPC_PONG = 0xa,
};

constexpr int MAX_PAYLOAD = 16 * 1024 * 1024;
constexpr int STREAM_MAX_BUF_LEN = 16 * 1024 * 1024;

class CaeWebSocket : public CaeSocket {
public:
    WebSocket *m_webSocket = nullptr;

    explicit CaeWebSocket(CaeTcpSocket *socket);

    explicit CaeWebSocket();

    bool InitCaeWebSocket();

    ~CaeWebSocket() override;

    virtual bool ParseHttpHeader();

    int GetFd() override;

    void SetSocketOption(uint32_t socketOption) override;

    int Send(void *uBuf, size_t len) override;

    int Recv(void *buf, size_t len) override;

    int SendCloseResponse();

    int SendClose();

    virtual int Send(void *uBuf, size_t len, enum WB_OPCODE wp);

    void EnableSend();

    void DisableSend();

    virtual bool Handshake();

    void CloseSocket() override;

    int GetSocketOption() override
    {
        if (m_tcpSocket != nullptr) {
            return m_tcpSocket->GetSocketOption();
        } else {
            return 0;
        }
    }

    void SetFd(int fd) override
    {
        if (m_tcpSocket != nullptr) {
            m_tcpSocket->SetFd(fd);
        }
    }

    void SetStatus(int status) override
    {
        if (m_tcpSocket != nullptr) {
            m_tcpSocket->SetStatus(status);
        }
    }

    int GetStatus() override
    {
        if (m_tcpSocket != nullptr) {
            return m_tcpSocket->GetStatus();
        } else {
            return -1;
        }
    }

    void SetSocketTimeout(long sec, long usec) override
    {
        if (m_tcpSocket != nullptr) {
            m_tcpSocket->SetSocketTimeout(sec, usec);
        }
    }

    int HandleWsCollectFrameKey(uint8_t c);

    int HandleWsFrameHdr1(uint8_t c);
protected:
    uint8_t *m_userBuf = nullptr;
    uint8_t *m_sendBuf = nullptr;
    uint8_t *m_rxBuf = nullptr;
    uint8_t *m_rxBufHead = nullptr;
    uint8_t m_opcode;
    uint8_t m_maskIdx;
    size_t m_rxBufLen;
    size_t m_userBufLen;
    size_t m_payloadLen;
    bool m_isMasked;
    bool m_isMaskZero;
    uint8_t m_mask[4];
    pthread_mutex_t m_lock;
    volatile bool m_sendFlag;
    int m_parsingState;
    int m_wsState;
    int Spill();
    int HandleWsFrameHdrLen(uint8_t c);
    int ParseHeader(uint8_t c);
    int ReadPayload();

private:
    CaeTcpSocket *m_tcpSocket = nullptr;

};

#endif
